import numpy as np


# 生成一个3x1的点矩阵
def gen_point_matrix(line_point):
    """
    :param line_point: 包含线段两个断点的顺序结构
    """
    return np.array([line_point[0], line_point[1], 1])


# 根据旋转角度获取旋转矩阵
def get_rotate_matrix(rotateAngle):
    """
    :param rotateAngle:旋转角度
    """
    return np.array([
        [np.cos(rotateAngle), -np.sin(rotateAngle),  0],
        [np.sin(rotateAngle),  np.cos(rotateAngle),  0],
        [0,                                      0,  1]
    ])


# 获取平移矩阵
def get_translation_matrix(trans_x, trans_y):
    """
    :param trans_x: x平移量
    :param trans_y: y平移量
    """
    return np.array([
        [1, 0, trans_x],
        [0, 1, trans_y],
        [0, 0,       1]
    ])


# 获取绕指定点旋转的旋转矩阵
def get_rotate_matrix_with_anchor(anchor, rotateAngle):
    """
    :param anchor: 旋转锚点
    :param rotateAngle: 旋转角度
    """
    return get_translation_matrix(anchor[0], anchor[1]).\
        dot(get_rotate_matrix(rotateAngle)).\
        dot(get_translation_matrix(-anchor[0], -anchor[1]))


# 获取缩放矩阵
def get_scale_matrix(scale_w, scale_h):
    """
    :param scale_w: 缩放宽度
    :param scale_h: 缩放高度
    """
    return np.array([
        [scale_w, 0, 0],
        [0, scale_h, 0],
        [0,       0, 1]
    ])


# 获取斜切矩阵
def get_shear_matrix(shear_x, shear_y):
    """
    :param shear_x: x轴斜切量
    :param shear_y: y轴斜切量
    """
    return np.array([
        [1, shear_x, 0],
        [shear_y, 1, 0],
        [0,       0, 1]
    ])


# 获取反射矩阵（对称矩阵）
def get_reflect_matrix(axis):
    """
    :param axis: 对称轴, 默认原点对称
                 特殊取值: 0    :关于原点对称
                         x    :关于 x 轴对称
                         y    :关于 y 轴对称
                         y=x  :关于直线 y=x（45°斜线） 对称
                         y=-x :关于直线 y=-x（-45°斜线） 对称
    """
    if isinstance(axis, str):
        if axis == "0":
            return np.array([
                [-1, 0, 0],
                [0, -1, 0],
                [0,  0, 1]
            ])
        elif axis == "x":
            return np.array([
                [1,  0, 0],
                [0, -1, 0],
                [0,  0, 1]
            ])
        elif axis == "y":
            return np.array([
                [-1, 0, 0],
                [0,  1, 0],
                [0,  0, 1]
            ])
        elif axis == "y=x":
            return np.array([
                [0, 1, 0],
                [1, 0, 0],
                [0, 0, 1]
            ])
        elif axis == "y=-x":
            return np.array([
                [0, -1, 0],
                [-1, 0, 0],
                [0,  0, 1]
            ])
    elif isinstance(axis, np.ndarray) or isinstance(axis, tuple) or isinstance(axis, list):
        x1 = axis[0][0]
        y1 = axis[0][1]
        x2 = axis[1][0]
        y2 = axis[1][1]
        a = y1 - y2
        b = x2 - x1
        c = (x1 * y2) - (x2 * y1)
        return (1 / (np.power(a, 2) + np.power(b, 2))) * np.array([
            [np.power(b, 2) - np.power(a, 2), -2 * a * b, -2 * a * c],
            [-2 * a * b, np.power(a, 2) - np.power(b, 2), -2 * b * c],
            [0,                                        0,          1]
        ])


# 变换直线
def transformLine0(line, transformMatrix):
    """
    :param line: 直线
    :param transformMatrix: 变换矩阵
    """
    transformed_matrix0 = transformMatrix.dot(gen_point_matrix(line[0]))
    transformed_matrix1 = transformMatrix.dot(gen_point_matrix(line[1]))
    return np.around(np.array([
        [transformed_matrix0[0], transformed_matrix0[1]],
        [transformed_matrix1[0], transformed_matrix1[1]]
    ]))


# 绕零点旋转直线指定角度
def rotateLineOrigin(line, rotateAngle):
    """
    :param line: 直线元组或直线矩阵(包含直线两个点的坐标)
                 例：((300, 500), (600, 300))
    :param rotateAngle: 旋转角度
    """
    return transformLine0(line, get_rotate_matrix(rotateAngle))


# 绕任意点旋转指定角度
def rotateLine(line, rotateAngle, anchor):
    """
    :param line: 直线元组或直线矩阵(包含直线两个点的坐标)
                 例：((300, 500), (600, 300))
    :param rotateAngle: 旋转角度
    :param anchor:      旋转锚点
    """
    return transformLine0(line, get_rotate_matrix_with_anchor(anchor, rotateAngle))


# 将直线以对称轴对称
def reflectLine(line, axis):
    """
    :param line: 直线元组或直线矩阵(包含直线两个点的坐标)
                 例：((300, 500), (600, 300))
    :param axis: 对称轴 (与 line 参数的格式相同，为一条直线)
                 特殊取值: 0    :关于原点对称
                         x    :关于 x 轴对称
                         y    :关于 y 轴对称
                         y=x  :关于直线 y=x（45°斜线） 对称
                         y=-x :关于直线 y=-x（-45°斜线） 对称
    """
    return transformLine0(line, get_reflect_matrix(axis))
